home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / NeXT-Icons / next-icon@gun.com / Apps / ImagePortfolio / PaletteCell.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-03  |  12.7 KB  |  497 lines

  1. // -------------------------------------------------------------------------------------
  2. // PaletteCell.m - image cell
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9.  
  10. extern "Objective-C" {
  11. #import <objc/objc.h>
  12. #import <appkit/appkit.h>
  13. #import <libc.h>
  14. #import <stdlib.h>
  15. #import <stdio.h>
  16. #import <string.h>
  17. #import <stdarg.h>
  18. #import <math.h>
  19. #import <dpsclient/wraps.h>
  20. #import "PaletteMatrix.h"
  21. #import "ImagePortfolio.h"
  22. #import "PaletteCell.h"
  23. #import "fileUtils.h"
  24. }
  25.  
  26. // -------------------------------------------------------------------------------------
  27. // delegate selectors
  28. #define    becameSELECTED        @selector(cellDidBecomeSelected:)
  29. #define    resignSELECTED        @selector(cellDidResignSelected:)
  30.  
  31. // -------------------------------------------------------------------------------------
  32. #define CELLOFFSET     ((cFlags1.bordered || cFlags1.bezeled)?(cFlags1.bordered?2.0:3.0):0.0)
  33. #define HIGHLIGHTED    ((BOOL)(cFlags1.highlighted || cFlags1.state))
  34.  
  35. // -------------------------------------------------------------------------------------
  36. @implementation PaletteCell
  37.  
  38. // -------------------------------------------------------------------------------------
  39. // support for multiple image types
  40. #define    maxFILETYPES    32
  41. static char    *imageFileExtn[maxFILETYPES + 1] = { 0 };
  42. static List    *imageClassList = (List*)nil;
  43. static int    imageClassCount = 0;
  44.  
  45. /* add image class type */
  46. + addImageClass:classId
  47. {
  48.   
  49.     /* initialize */
  50.     if (!imageClassList) imageClassList = [[[List alloc] initCount:1] empty];
  51.   
  52.     /* add class object */
  53.     [imageClassList addObject:classId];
  54.     imageClassCount = [imageClassList count];
  55.   
  56.     return self;
  57. }
  58.  
  59. /* add valid image extension */
  60. + addImageExtensions:(const char*)ext1, ...
  61. {
  62.     int        i;
  63.     char    *cp;
  64.     va_list    args;
  65.  
  66.      /* check for catch-all */
  67.     if (*ext1 == '*') {
  68.         for (i = 0; imageFileExtn[i]; i++) free(imageFileExtn[i]);
  69.         memset(imageFileExtn, 0, sizeof(imageFileExtn));
  70.         imageFileExtn[0] = NXCopyStringBuffer("*");
  71.         return self;
  72.     }
  73.     
  74.     /* find end of list */
  75.     for (i = 0; imageFileExtn[i]; i++);
  76.     
  77.     /* add first extension */
  78.     imageFileExtn[i++] = NXCopyStringBuffer(ext1);
  79.     
  80.     /* add all remaining extensions */
  81.     va_start(args, ext1);
  82.     for (;cp = va_arg(args,char*);) imageFileExtn[i++] = NXCopyStringBuffer(cp);
  83.     va_end(args);
  84.     
  85.     return self;
  86. }
  87.  
  88. /* return true if extension is valid */
  89. + (BOOL)validExtension:(const char*)fileName
  90. {
  91.     int        i;
  92.     char    *extn = (char*)XFileExtension((char*)fileName);
  93.     if (*imageFileExtn[0] == '*') return YES;
  94.     for (i = 0; imageFileExtn[i]; i++) if (!strcasecmp(extn, imageFileExtn[i])) return YES;
  95.     return NO;
  96. }
  97.  
  98. // -------------------------------------------------------------------------------------
  99. // new cell methods
  100.  
  101. /* old generic new cell */
  102. + new
  103. {
  104.     return [self newIconCell];
  105. }
  106.  
  107. /* generic new cell */
  108. + newIconCell
  109. {
  110.     self = [self newIconCell:(char*)nil];
  111.     [titleCell setStringValueNoCopy:""];
  112.     return self;
  113. }
  114.  
  115. /* new cell with title */
  116. + newIconCell:(const char *)aString
  117. {
  118.     return [[self allocFromZone:NXDefaultMallocZone()] initIconCell:aString];
  119. }
  120.  
  121. /* generic init */
  122. - init
  123. {
  124.     [self initIconCell:(char*)nil];
  125.     [titleCell setStringValueNoCopy:""];
  126.     return self;
  127. }
  128.  
  129. /* init with title */
  130. - initIconCell:(const char *)aString
  131. {
  132.  
  133.     /* init instance */
  134.     [super initIconCell:(char*)nil];
  135.     [self setStringValueNoCopy:""];
  136.     [self setAlignment:NX_CENTERED];
  137.     titleCell = [[Cell allocFromZone:[self zone]] initTextCell:aString];
  138.     [titleCell setAlignment:NX_CENTERED];
  139.     [self setBezeled:NO];
  140.  
  141.     /* init vars */
  142.     cFlags1.editable = NO;
  143.     cFlags1.selectable = NO;
  144.     cFlags2.noWrap = YES;
  145.     cFlags2._isLeaf = YES;
  146.     delegate = self;
  147.     dragMode = NO;
  148.   
  149.     imageId = (id)nil;
  150.     smallImage = (id)nil;
  151.     imagePath = (char*)nil;
  152.     imageMutex = mutex_alloc();
  153.   
  154.     return self;
  155. }
  156.  
  157. /* make an empty copy of this cell */
  158. - copy
  159. {
  160.     NXZone    *zone = [self zone];
  161.     id        tCell = titleCell;
  162.     self = [super copyFromZone:zone];
  163.     titleCell = [[tCell copyFromZone:zone] setFont:[tCell font]];
  164.     imageMutex = mutex_alloc();
  165.     imageId = (id)nil;
  166.     smallImage = (id)nil;
  167.     imagePath = (char*)nil;
  168.     return self;
  169. }
  170.  
  171. /* free this cell */
  172. - free
  173. {
  174.     if (imagePath) free(imagePath);
  175.     if (imageId) [imageId free];
  176.     if (smallImage) [smallImage free];
  177.     [titleCell free];
  178.     mutex_free(imageMutex);
  179.     return [super free];
  180. }
  181.  
  182. /* set matrix owner id */
  183. - setDelegate:anObject
  184. {
  185.     delegate = anObject;
  186.     return self;
  187. }
  188.  
  189. /* set drag mode */
  190. - setDragMode:(BOOL)flag
  191. {
  192.     dragMode = flag;
  193.     return self;
  194. }
  195.  
  196. // -------------------------------------------------------------------------------------
  197. // set cell size
  198.  
  199. - setCellSize:(const NXSize*)cellSize
  200. {
  201.     if (smallImage) { [smallImage free]; smallImage = (id)nil; }
  202.     return self;
  203. }
  204.  
  205. - setFont:fontId
  206. {
  207.     return [titleCell setFont:fontId];
  208. }
  209.   
  210. // -------------------------------------------------------------------------------------
  211. // image methods
  212.  
  213. /* load image specified in preset file name */
  214. - loadImageFile
  215. {
  216.     int        i;
  217.     id        tempId = (id)nil;
  218.  
  219.     /* validate load */
  220.     if (!imagePath) return (id)nil;            // no file specified
  221.     if (imageId) return imageId;            // already loaded
  222.  
  223.     /* try all image classes until we find one we like */
  224.     for (i = 0; !tempId && (i < imageClassCount); i++) {
  225.         id imClass = [imageClassList objectAt:i];
  226.         if (imClass == [NXImage class]) { // note: doesn't allow for subclassing NXImage
  227.             if (!(tempId = [[imClass alloc] initFromFile:imagePath])) continue;
  228.             if (![tempId lastRepresentation]) {
  229.                 [tempId free];
  230.                 tempId = (id)nil;
  231.                 continue;
  232.             }
  233.         } else {
  234.             int j;
  235.             NXSize size;
  236.             id repList = [imClass newListFromFile:imagePath];
  237.             if (!repList) continue;
  238.             if (![repList count]) { [repList free]; continue; }
  239.             [[repList objectAt:0] getSize:&size];
  240.             if (!size.width || !size.height) {
  241.                 [repList freeObjects];
  242.                 [repList free];
  243.                 continue;
  244.             }
  245.             tempId = [[NXImage alloc] init];
  246.             for (j = 0; j < (int)[repList count]; j++)
  247.                 [tempId useRepresentation:[repList objectAt:j]];
  248.             [repList free];
  249.         }
  250.         mutex_lock(imageMutex);
  251.         imageId = tempId;
  252.         mutex_unlock(imageMutex);
  253.         break;
  254.     }
  255.  
  256.     return tempId;
  257. }
  258.  
  259. /* open image (may be called from non-main thread) */
  260. - setImageFile:(const char *)filePath
  261. {
  262.     char    iName[MAXPATHLEN + 1], *p;
  263.   
  264.     /* free prior storage (actually this should never be necessary!) */
  265.     mutex_lock(imageMutex);
  266.     if (imagePath ) { free(imagePath);   imagePath = (char*)nil; }
  267.     if (imageId   ) { [imageId free];    imageId = (id)nil;      }
  268.     if (smallImage) { [smallImage free]; smallImage = (id)nil;   }
  269.     mutex_unlock(imageMutex);
  270.     
  271.     /* save filename and title */
  272.     imagePath = NXCopyStringBuffer(filePath);
  273.     strcpy(iName, XFileNameExtension(filePath));
  274.     if (p = rindex(iName, '.')) *p = 0;
  275.     [titleCell setStringValue:iName];    // OK since it can't draw yet
  276.   
  277.     /* open new image and make sure it was readable */
  278.     if (![self mainThreadPerform:@selector(loadImageFile) wait:YES]) return (id)nil;
  279.     return self;
  280.   
  281. }
  282.  
  283. /* return image id */
  284. - image
  285. {
  286.     id    image;
  287.     if (!imagePath) return (id)nil;
  288.     mutex_lock(imageMutex);
  289.     image = imageId;
  290.     mutex_unlock(imageMutex);
  291.     return image;
  292. }
  293.  
  294. /* return image representation */
  295. - imageRepresentation:(int)imageNum
  296. {
  297.     id        repList, image = [self image];
  298.     if (!image) return (id)nil;
  299.     repList = [image representationList];
  300.     return [repList objectAt:(imageNum % [repList count])];
  301. }
  302.  
  303. /* return path to image */
  304. - (const char*)imagePath
  305. {
  306.     return imagePath;
  307. }
  308.  
  309. /* return cell title */
  310. - (const char*)cellTitle
  311. {
  312.     return [titleCell stringValue];
  313. }
  314.  
  315. // -------------------------------------------------------------------------------------
  316.  
  317. - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
  318. {
  319.     NXRect rect = *aRect;
  320.     NXSize titleSize;
  321.     theSize->width = 1.0;
  322.     theSize->height = 1.0;
  323.     [titleCell calcCellSize:&titleSize inRect:&rect];
  324.     theSize->width += titleSize.width;
  325.     theSize->height = MAX(titleSize.height, theSize->height);
  326.     return self;
  327. }
  328.  
  329. // -------------------------------------------------------------------------------------
  330. // highlight cell
  331.   
  332. /* set selected state */
  333. - setState:(int)flag
  334. {
  335.     if (![self image]) return self;    // ignore if no image in cell
  336.     if ((!cFlags1.state && flag) || (cFlags1.state && !flag)) {
  337.         cFlags1.state = flag? YES : NO;
  338.         if (flag) {
  339.             if (delegate && [delegate respondsTo:@selector(cellBecameSelected:)])
  340.                 [delegate cellBecameSelected:self];
  341.         } else {
  342.             if (delegate && [delegate respondsTo:@selector(cellResignedSelected:)])
  343.                 [delegate cellResignedSelected:self];
  344.         }
  345.     }
  346.     return self;
  347. }
  348.  
  349. /* highlight and select */
  350. - highlight:(const NXRect*)cellFrame inView:controlView lit:(BOOL)flag
  351. {
  352.     if (![self image]) return self;    // ignore if no image in cell
  353.     if ((!cFlags1.highlighted && flag) || (cFlags1.highlighted && !flag)) {
  354.         cFlags1.highlighted = flag? YES : NO;
  355.         [self setState:cFlags1.highlighted];
  356.         [self drawSelf:cellFrame inView:controlView];
  357.     }
  358.     return self;
  359. }
  360.  
  361. /* return highlight state */
  362. - (BOOL)isHighlighted
  363. {
  364.     return HIGHLIGHTED;
  365. }
  366.  
  367. /* return selected state */
  368. - (BOOL)isSelected
  369. {
  370.     return (BOOL)cFlags1.state;
  371. }
  372.   
  373. // -------------------------------------------------------------------------------------
  374. // tool methods
  375.  
  376. /* resize image (only if newSize is smaller than original) (MAIN THREAD ONLY!) */
  377. - _resizeImage:image:(int)num toSize:(NXSize*)toSize
  378. {
  379.     float    aspect;
  380.     NXRect    r = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  381.     id        newImage;
  382.  
  383.     /* return if already within bounds */
  384.     if (!image) return (id)nil;
  385.     [image getSize:&r.size];
  386.     if ((r.size.width <= toSize->width) && (r.size.height <= toSize->height)) return (id)nil;
  387.   
  388.     /* calculate new bounds */
  389.     aspect = r.size.width / r.size.height;
  390.     if (r.size.width  > toSize->width )          r.size.width  = toSize->width ;
  391.     if (r.size.height > toSize->height)          r.size.height = toSize->height;
  392.     if (r.size.width  > r.size.height * aspect) r.size.width  = r.size.height * aspect;
  393.     if (r.size.height > r.size.width  / aspect) r.size.height = r.size.width  / aspect;
  394.  
  395.     /* create a new image */
  396.     newImage = [[NXImage alloc] initSize:&r.size];
  397.     if (!newImage) {
  398.         NXLogError("ImagePortfolio: Unable to init scaled image");
  399.         return (id)nil;
  400.     }
  401.     [newImage setFlipped:[image isFlipped]];
  402.   
  403.     /* draw scaled image */
  404.     PSgsave();
  405.     if ([newImage lockFocus]) {
  406.         [[self imageRepresentation:num] drawIn:&r];
  407.         [newImage unlockFocus];
  408.     } else {
  409.         [newImage free];
  410.         newImage = (id)nil;
  411.     }
  412.     PSgrestore();
  413.  
  414.     /* return resize image */
  415.     return newImage;
  416.   
  417. }
  418.   
  419. // -------------------------------------------------------------------------------------
  420. // smallImage support
  421.  
  422. /* calculate bounding size for small image */
  423. - calcImageSize:(NXSize*)imageSize forCellSize:(NXSize*)cellSize
  424. {
  425.     NXSize    tSize;
  426.     NXRect    cFrame = { {0.0, 0.0}, {0.0, 0.0} };
  427.     cFrame.size = *cellSize;
  428.     NXInsetRect(&cFrame, 1.0, 1.0);
  429.     [titleCell calcCellSize:&tSize inRect:&cFrame];
  430.     cFrame.size.height -= tSize.height + 2.0;
  431.     *imageSize = cFrame.size;
  432.     return self;
  433. }
  434.  
  435. // -------------------------------------------------------------------------------------
  436. // draw methods
  437.  
  438. - drawSelf:(const NXRect *)cellFrame inView:controlView
  439. {
  440.     return [self drawInside:cellFrame inView:controlView];
  441. }
  442.  
  443. - drawInside:(const NXRect *)cellFrame inView:controlView
  444. {
  445.     NXSize    tSize = {0.0, 0.0}, iSize;
  446.     NXRect    rect, cFrame = *cellFrame;
  447.     float    offset = CELLOFFSET;
  448.     BOOL    multi = NO;
  449.     id        image;
  450.  
  451.     /* check for drag mode */
  452.     if (dragMode) {
  453.         PSsetgray((NX_LTGRAY + NX_DKGRAY) / 2.0);
  454.         NXRectFill(cellFrame);
  455.         return self;
  456.     }
  457.     
  458.     /* draw border */
  459.     if (cFlags1.bordered) { PSsetgray(NX_BLACK); NXFrameRectWithWidth(cellFrame, 1.0); } else
  460.     if (cFlags1.bezeled ) { NXDrawGrayBezel(cellFrame, (NXRect*)nil); }
  461.   
  462.     /* inset rectangle and draw highlight */
  463.     NXInsetRect(&cFrame, offset, offset);
  464.     PSsetgray((HIGHLIGHTED)? NX_WHITE: NX_LTGRAY);
  465.     NXRectFill(&cFrame);
  466.     NXInsetRect(&cFrame, 1.0, 1.0);
  467.   
  468.     /* calc text size */
  469.     [titleCell calcCellSize:&tSize inRect:&cFrame];
  470.  
  471.     /* resize & draw image */
  472.     if ([self image]) {
  473.         rect = cFrame;
  474.         rect.size.height -= tSize.height + 2.0;
  475.         rect.origin.y += rect.size.height;
  476.         if ([[imageId representationList] count] > 1) multi = YES;
  477.         if (!smallImage) smallImage = [self _resizeImage:imageId:0 toSize:&rect.size];
  478.         image = (smallImage)? smallImage : imageId;
  479.         [image getSize:&iSize];
  480.         rect.origin.y -= (rect.size.height - iSize.height) / 2.0;
  481.         rect.origin.x += (rect.size.width  - iSize.width ) / 2.0;
  482.         [image composite:NX_SOVER toPoint:&rect.origin];
  483.     }
  484.   
  485.     /* draw image title cell */
  486.     rect = cFrame;
  487.     rect.origin.y += rect.size.height - tSize.height - 1;
  488.     rect.origin.x += floor((rect.size.width - tSize.width) / 2.0);
  489.     rect.size.width  = tSize.width;
  490.     rect.size.height = tSize.height;
  491.     [titleCell drawSelf:&rect inView:controlView];
  492.  
  493.     return self;
  494. }  
  495.  
  496. @end
  497.